from boardElements import *

class Game:
    '''
    Game functionality class
    '''
    def __init__(self):
        '''
        basic constructor
        '''
        self.rules = {TEAMS : True, BIG_CATH : False, PLAY_TWICE : False, NUM_PLAYERS : 2}
        self.totalScore = 0
        self.seriesWinner = TIE
        self.turn = P1  
        self.lastWinner = P1
        self.resetVariables()
        self.calculateScore()
        
    def resetVariables(self): 
        '''
        Initialization for game parameters
        '''
        self.p1Pieces = []
        self.p2Pieces = []
        self.p3Pieces = []
        self.p4Pieces = []
        if self.rules[NUM_PLAYERS] == 4:
            size = 14
            for name, p in P1_PIECES:
                self.p1Pieces.append(Piece(P1, name, p))
            for name, p in P2_PIECES:
                self.p2Pieces.append(Piece(P2, name, p))
            for name, p in P3_PIECES:
                self.p3Pieces.append(Piece(P1, name, p))
            for name, p in P4_PIECES:
                self.p4Pieces.append(Piece(P2, name, p))
        else:
            size = 10
            for name, p in LIGHT_PIECES:
                self.p1Pieces.append(Piece(P1, name, p))
            for name, p in DARK_PIECES:
                self.p2Pieces.append(Piece(P2, name, p))
        self.board = Board(size = size)
        if self.rules[BIG_CATH]:
            self.currentPiece = Piece(C,BC[0], BC[1])
        else:
            self.currentPiece = Piece(C,CA[0], CA[1])            
        self.rotate = N
        self.placed = False
        self.confirmSquares = []
        self.p1Selection = None
        self.p2Selection = None
        self.p3Selection = None
        self.p4Selection = None
        self.p1Score = 0
        self.p2Score = 0
        self.p3Score = 0
        self.p4Score = 0
        self.network = False
        self.started = False
        self.turnNumber = 0
        self.firstSkip = {P1 : False, P2 : False, P3 : False, P4 : False}
        
    def calculateScore(self):
        '''
        calculates the score
        '''
        self.p1Score = 0
        for p in self.p1Pieces:
            for s in p.squares:
                self.p1Score = self.p1Score + 1
        self.p2Score = 0
        for p in self.p2Pieces:
            for s in p.squares:
                self.p2Score = self.p2Score + 1
        self.p3Score = 0
        for p in self.p3Pieces:
            for s in p.squares:
                self.p3Score = self.p3Score + 1
        self.p4Score = 0
        for p in self.p4Pieces:
            for s in p.squares:
                self.p4Score = self.p4Score + 1
                
    def getWinner(self):
        '''
        figures out who is winning
        
        Returns: winner, delta score
        '''
        self.calculateScore()
        winner = []
        if min(self.p1Score, self.p2Score, self.p3Score, self.p4Score) == self.p1Score:
            winner.append(P1)
        if min(self.p1Score, self.p2Score, self.p3Score, self.p4Score) == self.p2Score:
            winner.append(P2)
        if min(self.p1Score, self.p2Score, self.p3Score, self.p4Score) == self.p3Score:
            winner.append(P3)
        if min(self.p1Score, self.p2Score, self.p3Score, self.p4Score) == self.p4Score:
            winner.append(P4)
        if len(winner) > 1:
            winner = [TIE]
        return winner[0], 0
    
    def pickleObjects(self):
        '''
        Packs up game objects for saving and sending to server
        
        Returns pickle object of all varaibles        
        '''
        objects = {}
        objects['board'] = self.board
        objects['p1Pieces'] = self.p1Pieces
        objects['p2Pieces'] = self.p2Pieces
        objects['p3Pieces'] = self.p3Pieces
        objects['p4Pieces'] = self.p4Pieces
        objects['turn'] = self.turn
        objects['lastWinner'] = self.lastWinner
        objects['currentPiece'] = self.currentPiece
        objects['rotate'] = self.rotate
        objects['placed'] = self.placed
        objects['confirmSquares'] = self.confirmSquares
        objects['p1Selection'] = self.p1Selection
        objects['p2Selection'] = self.p2Selection
        objects['p3Selection'] = self.p3Selection
        objects['p4Selection'] = self.p4Selection
        objects['p1Score'] = self.p1Score
        objects['p2Score'] = self.p2Score
        objects['p3Score'] = self.p3Score
        objects['p4Score'] = self.p4Score
        objects['totalScore'] = self.totalScore
        objects['seriesWinner'] = self.seriesWinner
        objects['rules'] = self.rules
        return pickle.dumps(objects)
    
    def unPickleObjects(self, stream):
        '''
        Unpacks up game objects from savefile or server
        '''
        objects = pickle.loads(stream)
        self.board = objects['board']
        self.p1Pieces = objects['p1Pieces']
        self.p2Pieces = objects['p2Pieces']
        self.p3Pieces = objects['p3Pieces']
        self.p4Pieces = objects['p4Pieces']
        self.turn = objects['turn']
        self.lastWinner = objects['lastWinner']
        self.currentPiece = objects['currentPiece']
        self.rotate = objects['rotate']
        self.placed = objects['placed']
        self.confirmSquares = objects['confirmSquares']
        self.p1Selection = objects['p1Selection']
        self.p2Selection = objects['p2Selection']
        self.p3Selection = objects['p3Selection']
        self.p4Selection = objects['p4Selection']
        self.p1Score = objects['p1Score']
        self.p2Score = objects['p2Score']
        self.p3Score = objects['p3Score']
        self.p4Score = objects['p4Score']
        self.totalScore = objects['totalScore']
        self.seriesWinner = objects['seriesWinner']
        self.rules = objects['rules']
    
    
    def undo(self):
        '''
        undo's last move as long as a piece wasn't captured      
        
        returns last piece
        '''
        piece = None
        if len(self.board.undoPieces) != 0:
            self.currentPiece = None
            piece = self.board.removePiece(self.board.undoPieces[len(self.board.undoPieces) - 1])
            self.rotate = piece.orientation
            self.board.updateBoard(self.turn)
            if piece.owner == C:
                self.currentPiece = piece
                self.turn = PREV_TURN(self.turn, self.rules[NUM_PLAYERS])
            if piece.owner == P1:
                self.p1Pieces.append(piece)
                self.turn = P1
            if piece.owner == P2:
                self.p2Pieces.append(piece)
                self.turn = P2
            if piece.owner == P3:
                self.p3Pieces.append(piece)
                self.turn = P3
            if piece.owner == P4:
                self.p4Pieces.append(piece)
                self.turn = P4
        return piece
    
    def checkValidMove(self, x, y):
        '''
        hook for checking valid moves in current board
        
        Returns: boolean valid status
        '''
        valid = self.board.isValidMove(self.currentPiece, x, y, self.board.squares)
        return valid
    
    def checkAvailableMoves(self):
        '''
        checks all available moves
        
        Returns: false if no moves are valid
        '''
        valid = False
        if self.turn == P1:
            pieces = self.p1Pieces
        if self.turn == P2:
            pieces = self.p2Pieces
        if self.turn == P3:
            pieces = self.p3Pieces
        if self.turn == P4:
            pieces = self.p4Pieces
        for p in pieces:
            for s in self.board.squares:
                for i in range(0,4):
                    p.setLocation(s.x, s.y, i)
                    valid = valid or self.board.isValidMove(p, s.x, s.y, self.board.squares)
        return valid
    
    def newGame(self, gameType):
        '''
        reinitializes variables and starts new game
        '''
        if self.started:
            self.gameOver()
        if gameType == 'Local':
            self.rules[NUM_PLAYERS] = 2
            self.network = False
            self.started = True    
        if gameType == 'Network': 
            self.rules[NUM_PLAYERS] = 2 
            print 'Network not supported yet, playing 2 player game'
            self.network = False
            self.started = True  
        if gameType == '4 Player Local':
            self.rules[NUM_PLAYERS] = 4 
            self.network = False
            self.started = True
        if self.lastWinner != TIE:
            self.turn = self.lastWinner            
        else:
            self.turn = GET_PLAYER_STR(random.randrange(self.rules[NUM_PLAYERS]))
        self.resetVariables() 
        self.started = True
        if self.turn == P1:
            self.p1Selection = self.currentPiece
        elif self.turn == P2:
            self.p2Selection = self.currentPiece
        elif self.turn == P3:
            self.p3Selection = self.currentPiece
        elif self.turn == P4:
            self.p4Selection = self.currentPiece
    
    def gameOver(self):
        '''
        ends game and updates series score
        '''
        # TODO: add 4 player functionality
        self.lastWinner, margin = self.getWinner()
        if self.lastWinner == P1:
            self.totalScore = self.totalScore + margin
        else:
            self.totalScore = self.totalScore - margin
        if self.totalScore > 0:
            self.seriesWinner = P1
        elif self.totalScore < 0:
            self.seriesWinner = P2
        else:
            self.seriesWinner = TIE            
                
    def skipTurn(self):
        '''
        Skip players turn
        '''
        self.firstSkip[self.turn] = True
        self.turn = NEXT_TURN(self.turn, self.rules[NUM_PLAYERS])
    
    def rotateCurrent(self, num):
        '''
        handles piece rotation on board
        
        Returns squares including placed piece
        '''
        newSquares = self.board.squares
        if self.started:
            y = num % self.board.size
            x = num / self.board.size
            if self.currentPiece != None:
                self.rotate = (self.rotate + 1) % 4
                self.currentPiece.setLocation(x, y, self.rotate)
            if self.checkValidMove(x, y):
                newSquares = self.board.placePiece(self.currentPiece, x, y)
        return newSquares
            
    def moveCurrent(self, num):
        '''
        handles piece movement on board
        
        Returns squares including placed piece
        '''
        newSquares = self.board.squares
        if not self.placed and self.currentPiece != None and self.started and num <= len(newSquares):
            y = num %  self.board.size
            x = num /  self.board.size
            newPiece = Piece(P, self.currentPiece.name, self.currentPiece.geometry)
            newPiece.orientation = self.rotate
            try:
                newSquares = self.board.placePiece(newPiece, x, y, dontCheck = True, realOwner = self.currentPiece.owner)
            except:
                pass
        return newSquares
        
    def checkPlaceCurrent(self, num):
        '''
        places piece in newSquares. Shows last move before player confirms placement
        
        Returns squares including placed piece
        '''
        newSquares = self.board.squares
        if self.started:
            y = num %  self.board.size
            x = num /  self.board.size
            if self.checkValidMove(x, y):
                self.confirmSquares = self.board.placePiece(self.currentPiece, x, y)
                self.placed = True
                newSquares = self.confirmSquares
        return newSquares
                
    def placeCurrent(self):
        '''
        places current piece on board and checks for territory captured
        '''
        self.turnNumber = self.turnNumber + 1
        self.board.pieces.append(self.currentPiece)
        self.board.undoPieces.append(self.currentPiece)
        self.turn = NEXT_TURN(self.turn, self.rules[NUM_PLAYERS])
        if self.currentPiece in self.p1Pieces:
            self.p1Pieces.remove(self.currentPiece)
        if self.currentPiece in self.p2Pieces:
            self.p2Pieces.remove(self.currentPiece)
        if self.currentPiece in self.p3Pieces:
            self.p3Pieces.remove(self.currentPiece)
        if self.currentPiece in self.p4Pieces:
            self.p4Pieces.remove(self.currentPiece)
        for i in range(0, len(self.confirmSquares)):
            self.board.squares[i].owner = self.confirmSquares[i].owner
            # TODO: add server calls here
        self.currentPiece = None
        self.placed = False
        removedPieces = self.board.updateBoard(self.turn)
        for p in removedPieces:
            if p.name != CA[0]:
                if self.turn == P1:
                    self.p1Pieces.append(p)
                if self.turn == P2:
                    self.p2Pieces.append(p)
                if self.turn == P3:
                    self.p3Pieces.append(p)
                if self.turn == P4:
                    self.p4Pieces.append(p)
        return removedPieces
        
    def selectPiece(self, player, piece):
        '''
        selects piece from player's pieces
        '''
        if player == P1:
            pieces = self.p1Pieces
        if player == P2:
            pieces = self.p2Pieces
        if player == P3:
            pieces = self.p3Pieces
        if player == P4:
            pieces = self.p4Pieces
        for p in pieces:
            if p.name == piece:
                selection = p
                if self.turn == player:
                    self.currentPiece = p
                    self.rotate = self.currentPiece.orientation
                else:
                    self.currentPiece = None
        if player == P1:
            self.p1Selection = selection
        if player == P2:
            self.p2Selection = selection
        if player == P3:
            self.p3Selection = selection
        if player == P4:
            self.p4Selection = selection

    def gameFinished(self):
        '''
        Determines if game is done (no valid moves or no pieces left)
        
        Returns: boolean true if game finished
        '''
        done = True
        for i in range(0, self.rules[NUM_PLAYERS]):
            player = GET_PLAYER_STR(i)
            if player == P1:
                noPieces = len(self.p1Pieces) == 0
            if player == P2:
                noPieces = len(self.p2Pieces) == 0
            if player == P3:
                noPieces = len(self.p3Pieces) == 0
            if player == P4:
                noPieces = len(self.p4Pieces) == 0
            if self.firstSkip[player] or noPieces:
                done = done and True
            else:
                done = False
        return done
                